package cn.cas.mango.service;

import cn.cas.mango.dto.HotSpotNews;
import cn.cas.mango.dto.Message;
import cn.cas.mango.dto.param.BaseTimeParam;
import cn.cas.mango.dto.param.HotSpotNewsParam;
import cn.cas.mango.dto.param.IdsParam;
import cn.cas.mango.service.samenews.SameNewsService;
import cn.cas.mango.util.RequestUtil.GetIdsRequest;
import cn.cas.mango.util.RequestUtil.GroupByRequest;
import cn.cas.mango.util.RequestUtil.HotListDetailRequest;
import cn.cas.mango.util.ResponseUtil.ResponseUtil;
import cn.cas.mango.util.TimeUtil;
import cn.cas.mango.util.paramCheck.HotListDetailParamCheckUtil;
import cn.cas.mango.util.singlepass.SinglePass;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch.core.search.Hit;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j
public class HotListDetailSearchService {

  @Autowired
  private HotListDetailParamCheckUtil checkUtil;
  @Autowired
  private ConfigService config;
  @Autowired
  private HotListDetailRequest HotListDetailRequest;
  @Autowired
  GroupByRequest groupByRequest;

  @Autowired
  private GetIdsRequest getIdsRequest;
  private Map<String, Hit<HotSpotNews>> wrapNewsMap(List<Hit<HotSpotNews>> newsList) {
    Map<String, Hit<HotSpotNews>> ret = new HashMap<>();
    for (Hit<HotSpotNews> hit: newsList) {
      HotSpotNews source = hit.source();
      String key = source.getId();
      ret.put(key, hit);
    }
    return ret;
  }

  private List<List<Hit<HotSpotNews>>> getReturnNews(Map<String, Hit<HotSpotNews>> newsMap,
                                                       Map<String, List<Hit<HotSpotNews>>> sameNewsList,
                                                       int from, int size) {
    List<List<Hit<HotSpotNews>>> ret = new ArrayList<>();
    List<String> coreKeyList = new ArrayList<>(sameNewsList.keySet());
    if (from > newsMap.size()) {
      return new ArrayList<>();
    }
    List<String> cutoffList = coreKeyList.subList(from, Math.min(from + size, coreKeyList.size()));
    for (String key: cutoffList) {
      List<Hit<HotSpotNews>> temp = new ArrayList<>();
      Hit<HotSpotNews> core = newsMap.get(key);
      temp.add(core);
      temp.addAll(sameNewsList.get(key));
      ret.add(temp);
    }
    return ret;
  }

  // 过滤关键词的黑名单
  private void filterBlackList(List<Hit<HotSpotNews>> list) {
    for (Hit<HotSpotNews> hit: list) {
      HotSpotNews source = hit.source();
      List<String> collectKeywords = Sets.difference(ResponseUtil.wrappedKeyword(source.getNewsKeywords()), config.getBlackList()).stream().collect(Collectors.toList());
      source.setNewsKeywords(collectKeywords);
    }
  }


  public Message getIds(IdsParam param) {
    String ids = param.getIds();
    String[] split = ids.trim().split(",");
    List<String> idsList = Arrays.asList(split);
    List<HotSpotNews> hotSpotNewsList = getIdsRequest.batchGetIds(idsList);

    JSONArray jsonArray = new JSONArray();
    for (HotSpotNews hotSpotNews: hotSpotNewsList) {
      String title = hotSpotNews.getNewsTitleZh();
      String content = hotSpotNews.getNewsContentZh();
      Object province = hotSpotNews.getFromProvince();
      String date = hotSpotNews.getNewsPublicDate();
      JSONObject jsonObject = new JSONObject();
      jsonObject.put("title", title);
      jsonObject.put("content", content);
      jsonObject.put("province", province);
      jsonObject.put("id", hotSpotNews.getId());
      jsonObject.put("date", date);
      jsonObject.put("type", config.getNumber2AreaCast().getOrDefault((String) hotSpotNews.getArea(), ""));
      jsonArray.add(jsonObject);
    }
    return Message.builder().code(200).msg("success").data(jsonArray).build();
  }

  public Message getGroupsByArea(BaseTimeParam param) {
    if (param.getStartDate() == null || param.getEndDate() == null){
      return Message.builder().code(400).msg("start date and end date can not be null").build();
    }
    List<Map<String, Object>> hotSpotNews = groupByRequest.getHotSpotNews(param, "area");
    Set<String> allProvince = config.getAllProvince();

    JSONArray inner = new JSONArray();
    JSONArray outer = new JSONArray();

    for (Map<String, Object> map: hotSpotNews) {
      String name = (String) map.get("name");
      Long count =  (Long) map.get("count");
      JSONObject temp = new JSONObject();
      temp.put("name", name);
      temp.put("count", count);
      if (allProvince.contains(name)) {
        inner.add(temp);
      } else {
        outer.add(temp);
      }
    }
    JSONObject area = new JSONObject();
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("国外", outer);
    jsonObject.put("国内", inner);
    area.put("area_list", jsonObject);
    return Message.builder().code(200).msg("success").data(area).build();
  }


  public Message getGroupsByType(BaseTimeParam param) {
    if (param.getStartDate() == null || param.getEndDate() == null){
      return Message.builder().code(400).msg("start date and end date can not be null").build();
    }
    List<Map<String, Object>> hotSpotNews = groupByRequest.getHotSpotNews(param, "ssly");

    JSONArray jsonArray = new JSONArray();
    for (Map<String, Object> map: hotSpotNews) {
      String name = (String) map.get("name");
      if (config.getNumber2AreaCast().containsKey(name)) {
        name = config.getNumber2AreaCast().get(name);
      }
      Long count =  (Long) map.get("count");
      JSONObject temp = new JSONObject();
      temp.put("name", name);
      temp.put("count", count);
      jsonArray.add(temp);
    }
    JSONObject area = new JSONObject();

    area.put("type_list", jsonArray);
    return Message.builder().code(200).msg("success").data(area).build();
  }




  public Message cluster_vector(HotSpotNewsParam param) {
    config.getGetAssembleHotNoVectorSubjects().clear();
    config.getGetAssembleHotNoVectorSubjects().addAll(new ArrayList<String>(){{add("娱乐");add("体育");}});
    log.info("no vector field: {}", config.getGetAssembleHotNoVectorSubjects());
    checkUtil.check(param);
    log.info("【uuid: {}】, query param: {}", param.getUuid(), param);
    List<Hit<HotSpotNews>> newsList = HotListDetailRequest.getNews(param);
    filterBlackList(newsList);
      Map<String, Hit<HotSpotNews>> newsMap = wrapNewsMap(newsList);
      TimeUtil timeTotal = new TimeUtil();
      Map<String, List<Hit<HotSpotNews>>> sameNewsList = new LinkedHashMap<>();

      if (config.isKeywordsCluster()) {

        SameNewsService sameNewsService = new SameNewsService(newsList, SortOrder.Desc, "_score");
        sameNewsList = sameNewsService.getSameNewsList(param.getLikeDegree(), param.getClusterKeywordsMode());
      } else {
        if (!newsMap.isEmpty()) {
          String core = null;
          for (String key: newsMap.keySet()) {core=key;break;}
          List<Hit<HotSpotNews>> temp = new ArrayList<>(newsMap.values());
          Hit<HotSpotNews> HotSpotNewsHit = newsMap.get(core);
          temp.remove(HotSpotNewsHit);
          sameNewsList.put(core, temp);
        }
      }
      log.info("【uuid: {}】, keywords cluster total cost: {}s ", param.getUuid(), timeTotal.getCost());

      List<List<Hit<HotSpotNews>>> returnNews = getReturnNews(newsMap, sameNewsList, 0, Integer.MAX_VALUE);
      List<List<HotSpotNews>> lists = deWrapNews(returnNews);
      List<List<HotSpotNews>> clusters;

    timeTotal.setStart();
    if (param.getClusterVectors().equals(true)) {
      log.info("【uuid: {}】, vector cluster start, aggWeight: {}", param.getUuid(), param.getAggWeight());
      clusters = new SinglePass(lists, param.getAggWeight(), config.getVectorSize(), param.getVectorField()).getClusters();
      log.info("【uuid: {}】, vector cluster total cost: {}s ", param.getUuid(), timeTotal.getCost());
    } else {
      clusters = lists;
      log.info("choose no vector cluster");
    }

    JSONArray jsonArray = new JSONArray();
    String province = param.getProvince();
    List<String> provinces = Arrays.asList(province.split(","));
    for (int i = 0; i < clusters.size() ;i++) {
      List<HotSpotNews> cluster = clusters.get(i);
      clusters.set(i, cluster.stream().filter(f -> provinces.contains(f.getFromProvince())).collect(Collectors.toList()));
    }

    clusters.sort((x,y)->-x.size() + y.size());
    for (List<HotSpotNews> cluster: clusters) {
      JSONObject jsonObject = wrapList(cluster);
      if (jsonObject.getInteger("simHotNum") > 0) {
        jsonArray.add(jsonObject);
      }
    }
    return Message.builder().code(200).data(jsonArray).build();
  }


  private JSONObject wrapList(List<HotSpotNews> list) {
    int hot = list.size();
    JSONObject coreObject = new JSONObject();
    coreObject.put("simHotNum", hot);
    JSONArray jsonArray = new JSONArray();
    List<String> keys = new ArrayList<>();
    for (HotSpotNews HotSpotNews: list) {keys.add(HotSpotNews.getId());}

    for (int i = 0; i < list.size(); i++) {
      HotSpotNews now = list.get(i);
      jsonArray.add(wrapCores(now));
    }
    coreObject.put("cluster", jsonArray);
    return coreObject;
  }


  private JSONObject wrapCores(HotSpotNews core) {
    String title = core.getNewsTitleZh();
    String id = core.getId();
    String newsContentZh = core.getNewsContentZh();
    String  newsPublicDate = core.getNewsPublicDate();
    Object newsKeywords = core.getNewsKeywords();
    JSONObject coreObject = new JSONObject();
    coreObject.put("title", title);
    coreObject.put("content", newsContentZh);
    coreObject.put("id", id);
    coreObject.put("news_public_date", newsPublicDate);
    coreObject.put("keywords", newsKeywords);
    coreObject.put("province", core.getFromProvince());
    coreObject.put("entities", core.getNewsEntities());
    coreObject.put("type", config.getNumber2AreaCast().getOrDefault((String) core.getArea(), ""));
    coreObject.put("place", core.getPlace());
    return coreObject;
  }



  private List<List<HotSpotNews>> deWrapNews(List<List<Hit<HotSpotNews>>> news) {
    List<List<HotSpotNews>> ret = new ArrayList<>();
    for (List<Hit<HotSpotNews>> hits : news) {
      List<HotSpotNews> collect = hits.stream().map(x -> x.source()).collect(Collectors.toList());
      ret.add(collect);
    }
    return ret;
  }





}
